package com.egzosn.infrastructure.database.jdbc;

import com.egzosn.infrastructure.database.jdbc.id.GenerationType;
import com.egzosn.infrastructure.database.utils.ReflectionUtils;
import com.egzosn.infrastructure.utils.common.Page;
import org.springframework.beans.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DataAccessException;
import org.springframework.dao.support.DataAccessUtils;
import org.springframework.jdbc.core.*;
import org.springframework.jdbc.support.JdbcUtils;
import org.springframework.jdbc.support.KeyHolder;

import java.io.Serializable;
import java.lang.reflect.InvocationTargetException;
import java.sql.*;
import java.util.*;

import static com.egzosn.infrastructure.database.utils.ReflectionUtils.getSuperClassGenricType;

/**
 * JDBC操作支持
 *
 * @author egan
 * @email egzosn@gmail.com
 * @date 2017/11/25
 */
public class SupportJdbcRepository<T> {

    @Autowired(required = false)
    protected JdbcTemplate jdbcTemplate;

    protected SingleTableEntityPersister<T> entityPersister;


    /**
     * 构造方法
     *
     * @param entityClass orm实体类型class
     */
    public SupportJdbcRepository(Class<T> entityClass) {
        entityPersister = new SingleTableEntityPersister<T>(entityClass);
    }


    /**
     * 构造方法
     */
    public SupportJdbcRepository() {
        entityPersister = new SingleTableEntityPersister<T>(ReflectionUtils.getSuperClassGenricType(getClass()));
    }


    @Autowired(required = false)
    public JdbcTemplate getJdbcTemplate() {
        return jdbcTemplate;
    }

    public void setJdbcTemplate(JdbcTemplate jdbcTemplate) {
        this.jdbcTemplate = jdbcTemplate;
    }


    /**
     * 获取表
     *
     * @return
     */
    protected String getTable() {
        return entityPersister.getTableName();
    }

    /**
     * 获取id 对应的列
     *
     * @return
     */
    protected String getIdColumn() {
        return entityPersister.getIdField().getColumn();
    }


    /**
     * 根据id获取ORM实体
     *
     * @param id
     *
     * @return
     */
    public T get(Serializable id) {
        return uniqueQueryEntity(String.format("%s where %s = ? ", SQLTools.getSelectSQL("*", getTable()), getIdColumn()), id);
    }

    /**
     * 根据id获取ORM实体
     *
     * @param ids 主键集合
     *
     * @return
     */
    public Collection<T> getAll(Collection<Serializable> ids) {
        return queryEntityList(String.format("%s where %s in( %s) ", SQLTools.getSelectSQL("*", getTable()), getIdColumn(), SQLTools.forQuestionMarkSQL(ids.size())), ids.toArray());
    }


    /**
     * 保存
     *
     * @param sql              sql
     * @param autoGeneratedKey 是否从数据库生成自增长的key, false则返回成功记录数
     * @param params           参数
     *
     * @return 主键对应的值
     */
    protected Serializable insert(String sql, boolean autoGeneratedKey, Object... params) {

        PreparedStatementCreator psc = new PreparedStatementCreator(sql, autoGeneratedKey, params);
        return jdbcTemplate.execute(psc, new PreparedStatementCallback<Serializable>() {
            @Override
            public Serializable doInPreparedStatement(PreparedStatement ps) throws SQLException, DataAccessException {
                if (autoGeneratedKey) {
                    KeyHolder keyHolder = GenerationType.AUTO.generation(ps, 1);
                    if (null != keyHolder) {
                        return keyHolder.getKey();
                    }
                }
                return psc.getRowCont();
            }
        });
    }

    /**
     * 保存
     *
     * @param sql              sql
     * @param autoGeneratedKey 是否从数据库生成自增长的key, false则返回成功记录数
     * @param params           参数
     *
     * @return 主键对应的值
     */
    protected Serializable insert(String sql, boolean autoGeneratedKey, Map<String, Object> params) {
        List<Object> paras = new ArrayList<>();
        return insert(SQLTools.forConverSQL(sql, params, paras), autoGeneratedKey, paras.toArray());
    }


    /**
     * 保存
     *
     * @param sql    sql
     * @param params 参数
     *
     * @return 主键对应的值
     */
    protected int update(String sql, Object... params) {
        return jdbcTemplate.update(sql, params);
    }

    /**
     * 保存
     *
     * @param sql    sql
     * @param params 参数
     *
     * @return 主键对应的值
     */
    protected int update(String sql, Map<String, Object> params) {
        List<Object> paras = new ArrayList<>();
        return update(SQLTools.forConverSQL(sql, params, paras), paras.toArray());
    }

    /**
     * 保存
     *
     * @param entity 实体
     *
     * @return 主键对应的值
     */
    public Serializable save(T entity) {
        if (null == entity) {
            return null;
        }
        return jdbcTemplate.execute(new PreparedStatementCreator(entityPersister.getSqlInsertString(entity)), new EntityPreparedStatementCallback<Serializable, T>(entityPersister, entity));
    }

    /**
     * 批量保存
     *
     * @param entity 实体集
     *
     * @return 主键对应的值
     */
    public List<Serializable> saveAll(Collection<T> entity) {
        if (null == entity || entity.isEmpty()) {
            return null;
        }
        return jdbcTemplate.execute(new PreparedStatementCreator(entityPersister.getSqlInsertString(entity.iterator().next())), new EntityPreparedStatementCallback<List<Serializable>, T>(entityPersister, entity));
    }


    /**
     * 更新
     *
     * @param entity 实体
     *
     * @return 成功记录数
     */
    public int update(T entity) {

        return jdbcTemplate.execute(new PreparedStatementCreator(entityPersister.getSqlUpdateByRowIdString(entity)), new EntityPreparedStatementCallback<Integer, T>(entityPersister, entity, false));
    }

    /**
     * 批量更新
     *
     * @param entity 实体集
     *
     * @return 成功记录数
     */
    public int update(Collection<T> entity) {

        return jdbcTemplate.execute(new PreparedStatementCreator(entityPersister.getSqlUpdateByRowIdString(entity.iterator().next())), new EntityPreparedStatementCallback<Integer, T>(entityPersister, entity, false));

    }


    /**
     * 删除
     *
     * @param sql    sql
     * @param params 参数
     *
     * @return 成功记录数
     */
    protected int delete(String sql, Object... params) {
        return jdbcTemplate.update(sql, params);

    }

    /**
     * 删除
     *
     * @param sql    sql
     * @param params 参数
     *
     * @return 成功记录数
     */
    protected int delete(String sql, Map<String, Object> params) {
        List<Object> paras = new ArrayList<>();
        return update(SQLTools.forConverSQL(sql, params, paras), paras.toArray());

    }

    /**
     * 删除
     *
     * @param id 主键标识
     *
     * @return 成功记录数
     */
    public int delete(Serializable id) {
        return delete(entityPersister.getSqlDeleteByRowIdString(), id);

    }

    /**
     * 批量删除
     *
     * @param ids 主键标识
     *
     * @return 成功记录数
     */
    public int delete(Collection<Serializable> ids) {
        return delete(entityPersister.getSqlDeleteByRowIdString(ids.size()), ids.toArray());
    }


    /**
     * 根据sql查询进行ORM映射集合
     *
     * @param sql    sql
     * @param values 属性值 以sql中的带":"的参数进行关联
     *               <code>
     *               String sql = "select * from table where name=:name";
     *               Map<String, Object> values = new HashMa<>();
     *               values.put("name", "张三");
     *               queryEntityList(sql, values);
     *               </code>
     *               {@link SQLTools}
     *
     * @return {@link SingleTableEntityPersister}
     */
    protected List<T> queryEntityList(String sql, Map<String, Object> values) {

        List<Object> paras = new ArrayList<>();
        return queryEntityList(SQLTools.forConverSQL(sql, values, paras), paras.toArray());
    }

    /**
     * 根据sql查询进行ORM映射集合
     *
     * @param sql    sql
     * @param values 数量可变的参数,按顺序绑定.
     *
     * @return {@link SingleTableEntityPersister}
     */
    protected List<T> queryEntityList(String sql, Object... values) {

        List<T> results = jdbcTemplate.query(sql, values, new RowMapper<T>() {
            /**
             * Extract the values for all columns in the current row.
             * <p>Utilizes public setters and result set metadata.
             * @see java.sql.ResultSetMetaData
             */
            @Override
            public T mapRow(ResultSet rs, int rowNum) throws SQLException {
                T mappedObject = BeanUtils.instantiateClass(SupportJdbcRepository.this.entityPersister.getEntityClass());

                ResultSetMetaData rsmd = rs.getMetaData();
                int columnCount = rsmd.getColumnCount();
                Map<String, Column> columns = entityPersister.getColumns();
                Map<String, Column> fields = entityPersister.getFields();
                //用于标记是否用数据库列充当key
                boolean flag = true;
                to:
                for (int index = 1; index <= columnCount; index++) {
                    String columnName = JdbcUtils.lookupColumnName(rsmd, index);
                    Column column = null;
                    if (flag) {
                        column = columns.get(columnName);
                        if (null == column) {
                            flag = false;
                            break to;
                        }
                    } else {
                        column = fields.get(columnName);
                    }
                    if (null == column) {
                        continue;
                    }
                    try {
                        column.getWriteMethod().invoke(mappedObject, rs.getObject(index, column.getType()));
                    } catch (IllegalAccessException e) {
                        e.printStackTrace();
                    } catch (InvocationTargetException e) {
                        e.printStackTrace();
                    }

                }
                return mappedObject;
            }
        });
        return results;
    }


    /**
     * 分页查询
     *
     * @param sql    sql
     * @param params 参数
     * @param page   第几页
     * @param rows   几行
     *
     * @return 返回的对象类型
     */
    protected List<T> queryEntityList(final String sql, int page, int rows, Object... params) {

        // TODO 2016/9/21 14:25 author: egan 获取对应结果集
        List<T> list = queryEntityList(sql + SQLTools.forPaginate(page, rows), params);
        return list;
    }


    /**
     * 查询自定义对象映射集合
     *
     * @param sql    查询语句
     * @param values 数量可变的参数,按顺序绑定.
     * @param <X>    类对应的实例
     *
     * @return {@link List}
     */
    public <X> List<X> queryList(String sql, Class<X> resultClass, Object... values) {
        String pageName = resultClass.getPackage().getName();
        if (pageName.contains("java.")) {
            return jdbcTemplate.queryForList(sql, values, resultClass);
        }
        return jdbcTemplate.query(sql, values, new BeanPropertyRowMapper<X>(resultClass));
    }


    /**
     * 分页查询
     * @param sql
     * @param resultClass 映射的对应类
     * @param params 参数
     * @param page 第几页
     * @param rows 几行
     * @param <X> 返回的对象类型
     * @return
     */
    protected <X> List<X> queryList(final String sql, Class<X> resultClass, int page, int rows, Object... params) {
      return new CreateQuery<X>(sql + SQLTools.forPaginate(page, rows), params).setResultClass(resultClass, resultClass == entityPersister.getEntityClass() ).list();
    }
    
    

    /**
     * 根据sql自定义对象映射集合
     *
     * @param sql         sql
     * @param resultClass 结果集的类
     * @param values      属性值 以sql中的带":"的参数进行关联
     * @param <X>         类对应的实例
     *                    <code>
     *                    String sql = "select * from table where name=:name";
     *                    Map<String, Object> values = new HashMa<>();
     *                    values.put("name", "张三");
     *                    queryEntityList(sql, values);
     *                    </code>
     *                    {@link SQLTools}
     *
     * @return {@link List}
     */
    public <X> List<X> queryList(String sql, Class<X> resultClass, Map<String, Object> values) {
        return  new CreateQuery<X>(sql, values).setResultClass(resultClass, resultClass == entityPersister.getEntityClass() ).list();
    }


    /**
     * 查询自定义对象映射集合
     *
     * @param sql    查询语句
     * @param values 数量可变的参数,按顺序绑定.
     *
     * @return {@link List}
     */
    public List<Map<String, Object>> queryMapList(String sql, Object... values) {
        return jdbcTemplate.queryForList(sql, values);
    }


    /**
     * 根据sql自定义对象映射集合
     *
     * @param sql    sql
     * @param values 属性值 以sql中的带":"的参数进行关联
     *               <code>
     *               String sql = "select * from table where name=:name";
     *               Map<String, Object> values = new HashMa<>();
     *               values.put("name", "张三");
     *               queryEntityList(sql, values);
     *               </code>
     *               {@link SQLTools}
     *
     * @return {@link List}
     */
    public List<Map<String, Object>> queryMapList(String sql, Map<String, Object> values) {

        List<Object> paras = new ArrayList<>();
        return queryMapList(SQLTools.forConverSQL(sql, values, paras), paras.toArray());
    }


    /**
     * 根据sql查询进行ORM映射集合
     *
     * @param sql    sql
     * @param values 数量可变的参数,按顺序绑定.
     *
     * @return {@link SingleTableEntityPersister}
     */
    protected T uniqueQueryEntity(String sql, Object... values) {
        return DataAccessUtils.singleResult(queryEntityList(sql, values));
    }

    /**
     * 根据sql查询进行ORM映射集合
     *
     * @param sql    sql
     * @param values 数量可变的参数,按顺序绑定.
     *
     * @return {@link SingleTableEntityPersister}
     */
    protected T uniqueQueryEntity(String sql, Map<String, Object> values) {
        return DataAccessUtils.singleResult(queryEntityList(sql, values));
    }


    /**
     * 查找一行对应的结果集
     *
     * @param query 创建查询记录对象
     * @param <X>   返回的对象类型
     *
     * @return 对象
     */
    public <X> X uniqueQuery(CreateQuery<X> query, Class<X> resultClass) {
        return query.setResultClass(resultClass, resultClass == entityPersister.getEntityClass()).uniqueResult();

    }

    /**
     * 查找出一行对应的结果集
     *
     * @param sql
     * @param resultClass 对象映射
     * @param params      参数
     * @param <X>         返回的对象类型
     *
     * @return
     */
    public <X> X uniqueQuery(final String sql, Class<X> resultClass, Map<String, Object> params) {
        return uniqueQuery(new CreateQuery<X>(sql, params), resultClass);
    }

    /**
     * 查找出单一对应的结果集
     *
     * @param sql
     * @param resultClass 对象映射
     * @param params      参数
     * @param <X>         返回的对象类型
     *
     * @return
     */
    public <X> X uniqueQuery(final String sql, Class<X> resultClass, Object... params) {
        return uniqueQuery(new CreateQuery<X>(sql, params), resultClass);
    }
    /**
     * 查找出一行对应的结果集
     *
     * @param sql
     * @param params      参数
     *
     * @return
     */
    public Map<String, Object> uniqueQuery(final String sql, Map<String, Object> params) {
        return uniqueQuery(new CreateQuery<Map<String, Object>>(sql, params), null);
    }

    /**
     * 查找出单一对应的结果集
     *
     * @param sql
     * @param params      参数
     *
     * @return
     */
    public Map<String, Object> uniqueQuery(final String sql, Object... params) {
        return uniqueQuery(new CreateQuery<Map<String, Object>>(sql, params), null);
    }

    /**
     * 查询记录数
     *
     * @param createQuery
     *
     * @return
     */
    public long count(CreateQuery createQuery) {
        createQuery.setIsCount(true);
        Object object = createQuery.uniqueResult();
        if (!(object instanceof Number)) {
            return 0;
        }
        return ((Number) object).longValue();
    }

    /**
     * 查询记录数
     *
     * @param sql
     * @param params
     *
     * @return
     */
    public long countSQL(String sql, Map<String, Object> params) {
        return count(new CreateQuery(sql, params));
    }

    /**
     * 查询记录数
     *
     * @param sql
     * @param params
     *
     * @return
     */
    public long countSQL(String sql, Object... params) {
        return count(new CreateQuery(sql, params));
    }


    /**
     * 分页查询
     *
     * @param sql
     * @param params 参数
     * @param page   第几页
     * @param rows   几行
     *
     * @return 返回的对象类型
     */
    protected Page<T> pageQueryEntity(final String sql, Map<String, Object> params, int page, int rows) {
        List<Object> values = new ArrayList<>();
        return pageQueryEntity(SQLTools.forConverSQL(sql, params, values), page, rows, values.toArray());
    }


    /**
     * 分页查询
     *
     * @param sql    sql
     * @param params 参数
     * @param page   第几页
     * @param rows   几行
     *
     * @return 返回的对象类型
     */
    protected Page<T> pageQueryEntity(final String sql, int page, int rows, Object... params) {
        long lo = countSQL(sql, params);
        if (0 == lo) {
            return new Page<>();
        }

        // TODO 2016/9/21 14:25 author: egan 获取对应结果集
        List<T> list = queryEntityList(sql, page, rows, params);
        return new Page<T>(page, list.size(), lo, list);
    }

    /**
     * 分页查询
     *
     * @param sql    sql
     * @param params 参数
     *
     * @return 返回的对象类型
     */
    protected Page<T> pageQueryEntity(final String sql, Map<String, Object> params) {
        List<Object> values = new ArrayList<>();
        return pageQueryEntity(SQLTools.forConverSQL(sql, params, values), values.toArray());
    }


    /**
     * 分页查询
     *
     * @param sql    sql
     * @param params 参数
     *
     * @return 返回的对象类型
     */
    protected Page<T> pageQueryEntity(final String sql, Object... params) {
        long lo = countSQL(sql, params);
        if (0 == lo) {
            return new Page<>();
        }

        // TODO 2016/9/21 14:25 author: egan 获取对应结果集
        List<T> list = queryEntityList(sql, params);
        return new Page<T>(1, list.size(), lo, list);
    }



    /**
     * 分页查询
     * @param sql
     * @param resultClass 映射的对应类
     * @param params 参数
     * @param page 第几页
     * @param rows 几行
     * @param <X> 返回的对象类型
     * @return
     */
    protected <X> Page<X> pageQuerySQL(final String sql, Class<X> resultClass, Map<String, Object> params, int page, int rows) {
        List<Object> values = new ArrayList<>();
        return pageQuerySQL(SQLTools.forConverSQL(sql, params, values), resultClass, page, rows, values.toArray());
    }


    /**
     * 分页查询
     * @param sql
     * @param resultClass 映射的对应类
     * @param params 参数
     * @param page 第几页
     * @param rows 几行
     * @param <X> 返回的对象类型
     * @return
     */
    protected <X> Page<X> pageQuerySQL(final String sql, Class<X> resultClass, int page, int rows, Object... params) {
        long lo = countSQL(sql, params);
        if (0 == lo){ return new Page<>();}

        // TODO 2016/9/21 14:25 author: egan 获取对应结果集
        List<X> list = queryList(sql, resultClass, page, rows, params);
        return new Page<X>(page, list.size(), lo, list);
    }


    /**
     * 分页查询
     * @param sql
     * @param resultClass 映射的对应类
     * @param params 参数
     * @param <X> 返回的对象类型
     * @return
     */
    protected <X> Page<X> pageQuerySQL(final String sql, Class<X> resultClass, Map<String, Object> params) {
        List<Object> values = new ArrayList<>();
        return pageQuerySQL(SQLTools.forConverSQL(sql, params, values), resultClass, values.toArray());
    }


    /**
     * 分页查询
     * @param sql
     * @param resultClass 映射的对应类
     * @param params 参数
     * @param <X> 返回的对象类型
     * @return
     */
    protected <X> Page<X> pageQuerySQL(final String sql, Class<X> resultClass, Object... params) {
        long lo = countSQL(sql, params);
        if (0 == lo) {
            return new Page<>();
        }

        // TODO 2016/9/21 14:25 author: egan 获取对应结果集
//
        List<X> list = new CreateQuery<X>(sql, params).setResultClass(resultClass, resultClass == entityPersister.getEntityClass() ).list();
        return new Page<X>(1, list.size(), lo, list);
    }



    /**
     * 创建查询记录集
     */
    public class CreateQuery<X> {
        private String sql;
        private Object[] paras;
        Class<X> resultClass;//主要用于sql查询结果集映射的对象
        boolean isEntity = true;
        boolean isCount = false;


        public CreateQuery(String sql, Object... paras) {
            this.sql = sql;
            this.paras = paras;
        }

        public CreateQuery(String sql, Map<String, Object> attr) {
            List<Object> paras = new ArrayList<>();
            this.sql = SQLTools.forConverSQL(sql, attr, paras);
            this.paras = paras.toArray();
        }

        public CreateQuery<X> setResultClass(Class<X> resultClass, boolean isEntity) {
            this.resultClass = resultClass;
            this.isEntity = isEntity;
            return this;
        }

        public CreateQuery<X> setResultClass(Class<X> resultClass, boolean isEntity, boolean isCount) {
            this.resultClass = resultClass;
            this.isEntity = isEntity;
            this.isCount = isCount;
            return this;
        }
        public CreateQuery<X> setIsCount( boolean isCount) {
            this.isCount = isCount;
            return this;
        }

        public String toSQL() {
            return sql;
        }

        public Object[] getParas() {
            return paras;
        }

        public List<X> list() {
            if (isEntity) {
                return (List<X>) SupportJdbcRepository.this.queryEntityList(sql, paras);
            }
            if (null == resultClass || resultClass.isAssignableFrom(Map.class)) {
                return (List<X>) SupportJdbcRepository.this.queryMapList(sql, paras);
            }
            return SupportJdbcRepository.this.queryList(sql, resultClass, paras);
        }

        public X uniqueResult() {

            if (isCount) {
                return jdbcTemplate.queryForObject(SQLTools.getCountSQL(sql), paras, null == resultClass ? (Class<X>) Long.class : resultClass);
            }
            if (!sql.toUpperCase().contains(" LIMIT ")) {
                sql += " limit 1";
            }
            return DataAccessUtils.singleResult(list());
        }


    }


}
